package com.lovesher.payment.service.plugin.wechat;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;

import com.lovesher.payment.common.enums.NotifyMethod;
import com.lovesher.payment.common.enums.PayStatus;
import com.lovesher.payment.common.enums.PayType;
import com.lovesher.payment.common.resposity.bean.AccountRechargeDTO;
import com.lovesher.payment.common.resposity.bean.PaymentSerial;
import com.lovesher.payment.common.resposity.bean.PluginConfig;
import com.lovesher.payment.common.resposity.bean.PluginConfigAttribute;
import com.lovesher.payment.service.resposity.bean.PaymentInterface;
import com.lovesher.payment.service.resposity.dao.PaymentDao;
import com.lovesher.payment.service.resposity.mapper.PluginConfigAttributeMapper;

/**
 * @Title: weixinRefundPlugin.java
 * 
 * @Description: 微信IOS退款插件
 * 
 * @author 
 * 
 * @version V1.0
 */
@Component("weixinIOSRefundPlugin")
public class WeixinIOSRefundPlugin extends PaymentInterface {

	private static Log log = LogFactory.getLog(WeixinIOSRefundPlugin.class);
	
	@Resource
	private PluginConfigAttributeMapper pluginConfigAttributeMapper;
	@Resource
	private PaymentDao paymentDao;
	@Override
	public String getName() {
		return "微信退款插件";
	}

	@Override
	public String getVersion() {
		return "1.0";
	}

	@Override
	public String getAuthor() {
		return "";
	}

	@Override
	public String getSiteUrl() {
		return "https://pay.weixin.qq.com";
	}

	@Override
	public String getRequestUrl() {
		return "https://api.mch.weixin.qq.com/secapi/pay/refund";
	}

	@Override
	public RequestMethod getRequestMethod() {
		return RequestMethod.post;
	}

	@Override
	public String getRequestCharset() {
		return "UTF-8";
	}

	/**
	 * {"transaction_id":"4200000297201904101521021504",
	 * "nonce_str":"2d84560f25c014896217b72da6aa011c",
	 * "refund_status":"SUCCESS",
	 * "out_refund_no":"2019040915574102840086",
	 * "settlement_refund_fee":"500",
	 * "success_time":"2019-04-10 09:44:38",
	 * "mch_id":"1521680121",
	 * "refund_recv_accout":"支付用户零钱通",
	 * "refund_id":"50000000062019041009048929732",
	 * "out_trade_no":"2019040915574102840086",
	 * "req_info":"IAXvCtPAQ+ChfawHITsWAizcegQUIz7uQgMwpv+5MsZln8hdnIQZdB1F6KLiOsFrHH0o+GsZLxc8msuGLYvMRbzqPBGcgaCHAjnKElVz8BHoyQnydJ4TA3vZ7iTX8TFFnDV1avk2vsVvqmLFVT2v2HcQ9sY2N2iC15H6GoBc/i8v8Ct/Husvh1kB3A1p4TZJ2XF3spSiWLxIKA3GbOIeVCxbxv5ghuWKiQQ0qJUsCcKNmrRxBtOxTfVq1Lf/JjGS7SNK4exJxanHS3pIj6b9gTus/1fWZJmd3T2tP06IiKq5lsdMsGfagoRTrLVHQMtma5SZFF7rMu4O5PRU0YkyAa8Eeslvv3ZtirpjU16grIOiX37Ap//nYgT1EzPA+Bu1VKSVOpa44sCfHYo9Xaa+4/jS38KQOG7EHwh/Q15kE3MPU2FuIkPTtuKPX9RB6p3vkBFNyNJ9eFY8Kwkt+K8UlL5Oah55lfRgLnEekkZcD2whmnqucWsWLQYhaGDqruAoSHTIMPIHaIf6FQzBJbVVr8o/dzG2fh/YrAAHr1i8G0mCO8qfM63Vg2zY4n80nAqcVp7stAKpvxQgQLnRB0LafpBcpA1jNAw/SCDBL2AWjG1nI5Tyb1XAOmm7U5rsjOiQvsNLmUokeKVLVYzWzkDPnKVugtrTGnB00bDfgj5IqRWXSJQhgD4BEvz2miP35NBEjcytCVIgH9dbPAkLyxmbL/bOTHiZh1TvQWDxsGH/+puG7vvgNC6i3oEYM0m7qdtUjU17RJA0bi9MIVmietC5grmWx0ywZ9qChFOstUdAy2bd8PH4Dj9PGBRiXCfYcYtpgInMcvfuYabRtr+s5Dntr8Xmf3WHKkvlVjviulMr1VJ+n28X7MMReStTuJxvbFlpK9O9i8VPwkRtNsWEHVVfIczt4+Sb2R6lXR7Yodd+yuoCP1bexrFtT8p5HgifdiPi9JSKpo2rvjYhr6c7GqbkxQsIvNwNpvDCh1A3H49L55fK9Y/I1riN/NPg4oBeWvRU+TsGVlZORzyBbOf2tqsS7aO/6GVQxYGbIyRglRb9oH5OqwgyaAOjfP2uyxkFZYaY","refund_account":"REFUND_SOURCE_RECHARGE_FUNDS",
	 * "appid":"wx1a2a749c84728e25",
	 * "refund_fee":"500",
	 * "total_fee":"500",
	 * "settlement_total_fee":"500",
	 * "return_code":"SUCCESS",
	 * "refund_request_source":"API"
	 * }
	 */
	@Override
	public Map<String,Object> verifyNotify(String sn, NotifyMethod notifyMethod, HttpServletRequest request,PaymentSerial paymentSerial) {
		Map<String,Object> resultMap = new HashMap<String,Object>();
		String result = "";
		InputStream inStream = null;
		try {
			inStream = request.getInputStream();
			ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			outSteam.close();
			inStream.close();
			result = new String(outSteam.toByteArray(), "utf-8");
		} catch (IOException e) {
			log.error("",e);
			resultMap.put("success", Boolean.FALSE);
			return resultMap;
		}
		log.info(getName() + "通知参数验证,通知报文为:" + result);
		Map<String, String> map = null;

		if (StringUtils.isNotEmpty(result)) {
			request.setAttribute("resString", result);
			resultMap.put("returnText", result);
			try {
				map = XmlUtils.getMapFromXML(result);
			} catch (Exception e) {
				log.error(getName() + "通知参数验签，通知参数:" + map + ",转换为map异常，异常信息:" , e);
				resultMap.put("success", Boolean.FALSE);
				return resultMap;
			}
			log.info(getName() + "通知参数验签，通知参数:" + result);
			String return_code = map.get("return_code");
			if (!"SUCCESS".equals(return_code)) {
				log.info(getName() + "通知验签不通过，通知参数:验证签名不通过:" + map);
				resultMap.put("success", Boolean.FALSE);
				resultMap.putAll(map);
			} else {
				PluginConfig pluginConfig = getPluginConfig();
				Map<String, Object> pluginConfigMap = getPluginConfigMap(pluginConfig);
				String mch_id = map.get("mch_id");
				String appid = map.get("appid");
				if(!mch_id.equals(pluginConfigMap.get("mch_id").toString())){
					resultMap.put("success", Boolean.FALSE);
					resultMap.put("returnText", "商户号错误" + mch_id );
					return resultMap;
				}
				if(!appid.equals(pluginConfigMap.get("appid").toString())){
					resultMap.put("success", Boolean.FALSE);
					resultMap.put("returnText", "appid错误" + appid );
					return resultMap;
				}
				try {
					String req_info = decryptData(map.get("req_info"), pluginConfigMap.get("key").toString());
					resultMap.putAll(XmlUtils.getMapFromXML(req_info));
					if(resultMap.get("refund_status").equals("SUCCESS")){
						resultMap.put("success", Boolean.TRUE);
						resultMap.put("returnText", "退款成功，金额"+ map.get("settlement_refund_fee"));
					}else{
						resultMap.put("success", Boolean.FALSE);
						resultMap.put("returnText", "退款失败，金额"+ map.get("settlement_refund_fee"));
					}
					resultMap.remove("req_info");
				} catch (Exception e) {
					resultMap.put("success", Boolean.FALSE);
					resultMap.put("returnText", "解密失败");
				}
			}
		}else {
			resultMap.put("success", Boolean.FALSE);
		}
		return resultMap;
	}
	private static String decryptData(String base64Data,String key) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
    	SecretKeySpec secretKey = new SecretKeySpec(MD5Util.MD5Encode(key, "UTF-8").toLowerCase().getBytes(), "AES");
		cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return new String(cipher.doFinal(Base64Util.decode(base64Data)));
    }

	@Override
	public String getNotifyMessage(String sn, NotifyMethod notifyMethod, HttpServletRequest request) {
		if (notifyMethod == NotifyMethod.async) {
			return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
		}
		return null;
	}

	@Override
	public Integer getTimeout() {
		return 21600;
	}

	/**
	 * 构造签名
	 * 
	 * @param params
	 * @param encode
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String createSign(Map<String, String> params, boolean encode) {
		Set<String> keysSet = params.keySet();
		Object[] keys = keysSet.toArray();
		Arrays.sort(keys);
		StringBuffer temp = new StringBuffer();
		boolean first = true;
		for (Object key : keys) {
			if (first) {
				first = false;
			} else {
				temp.append("&");
			}
			temp.append(key).append("=");
			Object value = params.get(key);
			String valueString = "";
			if (null != value) {
				valueString = value.toString();
			}
			if (encode) {
				try {
					temp.append(URLEncoder.encode(valueString, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			} else {
				temp.append(valueString);
			}
		}
		return temp.toString();
	}

	/**
	 * @param serialSn 为之前的充值我们平台的流水号
	 * @param accountRechargeDTO.getOrderSn() 是退款流水号
	 * 
	 * @param accountRechargeDTO.getMoney() 是充值的金额 单位元
	 * 
	 * @param accountRechargeDTO.getFee() 退款总金额，单位元
	 * 
	 * @param accountRechargeDTO.getRemark()  若商户传入，会在下发给用户的退款消息中体现退款原因
	 * 
	 */
	@Override
	public Map<String, String> getParameterMap(AccountRechargeDTO accountRechargeDTO, String serialSn) {
		PluginConfig pluginConfig = getPluginConfig();
		Map<String, Object> pluginConfigMap = new HashMap<String, Object>();
		List<PluginConfigAttribute> pluginConfigAttributes = pluginConfigAttributeMapper.findByPluginId(pluginConfig.getId());
		for (int i = 0; i < pluginConfigAttributes.size(); i++) {
			PluginConfigAttribute pluginConfigAttribute = pluginConfigAttributes.get(i);
			pluginConfigMap.put(pluginConfigAttribute.getName(), pluginConfigAttribute.getAttributes());
		}

		SortedMap<String, String> parameterMap = new TreeMap<String, String>();
		Integer amount = accountRechargeDTO.getMoney().multiply(new BigDecimal(100)).intValue();
		parameterMap.put("appid", pluginConfigMap.get("appid").toString());// 微信分配的公众账号ID
		parameterMap.put("mch_id", pluginConfigMap.get("mch_id").toString());// 微信支付分配的商户号
		parameterMap.put("nonce_str", RandomStringUtils.random(32, "abcdefghijklmnopqrstuvwxyz0123456789"));// 随机字符串，不长于32位。
		parameterMap.put("out_trade_no", serialSn); // 商户系统内部的订单号,32个字符内、可包含字母
		parameterMap.put("out_refund_no", accountRechargeDTO.getOrderSn()); // 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
		parameterMap.put("total_fee", amount.toString()); // 订单总金额，单位为分，只能为整数，详见支付金额
		parameterMap.put("refund_fee", accountRechargeDTO.getFee().setScale(2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(0).toString());//退款总金额，订单总金额，单位为分，只能为整数，详见支付金额
		parameterMap.put("notify_url", super.getRefundNotifyUrl(serialSn, accountRechargeDTO.getUserId(), NotifyMethod.async,PayType.valueOf(accountRechargeDTO.getType())));// 接收微信支付异步通知回调地址
		parameterMap.put("refund_desc", accountRechargeDTO.getRemark());// 若商户传入，会在下发给用户的退款消息中体现退款原因
		String string1 = createSign(parameterMap, false);
		String stringSignTemp = string1 + "&key=" + pluginConfigMap.get("key").toString();
		log.info("待加密字符串:" + stringSignTemp);
		String signValue = DigestUtils.md5Hex(stringSignTemp).toUpperCase();
		parameterMap.put("sign", signValue);
		String result = WeixinPayUtils.sendPost(getRequestUrl(), parameterMap);
		if (StringUtils.isEmpty(result)) {
			return null;
		}
		Map<String, String> resultMap = new HashMap<String, String>();
		try {
			resultMap = XmlUtils.getMapFromXML(result);
		} catch (Exception e) {
			log.error("",e);
		}
		String return_code = resultMap.get("return_code").toString();
		String result_code = resultMap.get("result_code").toString();
		if (!"SUCCESS".equals(return_code) || !"SUCCESS".equals(result_code) || null == resultMap.get("transaction_id")) {
			resultMap.put("success", Boolean.FALSE.toString());
			resultMap.put("message", resultMap.get("err_code_des"));
		} else {
			resultMap.put("success", Boolean.TRUE.toString());
			resultMap.put("message", resultMap.get("transaction_id"));
		}
		return resultMap;
	}
	


	/**
	 * 使用 Map按key进行排序
	 * @param map
	 * @return
	 */
	public static Map<String, String> sortMapByKey(Map<String, String> map) {
		if (map == null || map.isEmpty()) {
			return null;
		}

		Map<String, String> sortMap = new TreeMap<String, String>(
				);

		sortMap.putAll(map);

		return sortMap;
	}
	

	@Override
	public void paySucess(PaymentSerial paymentSerial, String returnText, PayStatus payStatus,
			HttpServletRequest request) {
		paymentDao.handle(paymentSerial, returnText, payStatus);
	}

	@Override
	public Boolean queryOrderResult(PaymentSerial paymentSerial) {
		// 根据充值流水号查询支付状态
		if (null != paymentSerial && paymentSerial.getStatus() == PayStatus.INIT.getValue()) {
			PluginConfig pluginConfig = getPluginConfig();
			// 数据库中获取支付相关参数
			Map<String, Object> pluginConfigMap = getPluginConfigMap(pluginConfig);
			log.info("【订单查询:" + paymentSerial.getOrderSn() + "userId" + paymentSerial.getUserId() + "】开始封装参数...");
			SortedMap<String, String> parameterMap = new TreeMap<String, String>();
			String appid = pluginConfigMap.get("appid").toString();
			String mch_id = pluginConfigMap.get("mch_id").toString();
			String out_trade_no = paymentSerial.getOrderSn().toString();
			String nonce_str = RandomStringUtils.random(32, "abcdefghijklmnopqrstuvwxyz0123456789");
			parameterMap.put("appid", appid);
			parameterMap.put("mch_id", mch_id);
			parameterMap.put("out_refund_no", out_trade_no);//我们的退款流水号
			parameterMap.put("nonce_str", nonce_str);
			String strCreate = createSign(parameterMap, false) + "&key=" + pluginConfigMap.get("key").toString();
			parameterMap.put("sign",  DigestUtils.md5Hex(strCreate).toUpperCase());
			try {
				String result = WeixinPayUtils.sendPost("https://api.mch.weixin.qq.com/pay/refundquery", parameterMap);
				if (StringUtils.isEmpty(result)) {
					return null;
				}
				Map<String, String> resultMap = new HashMap<String, String>();
				resultMap = XmlUtils.getMapFromXML(result);
				if (resultMap.get("return_code").equalsIgnoreCase("SUCCESS") && resultMap.get("result_code").equalsIgnoreCase("SUCCESS")) {
					if(resultMap.get("refund_status_0").equals("SUCCESS")){
						log.info("tradeNo："+paymentSerial.getOrderSn()+"  refund success");
						return true;
						//paymentDao.handle(paymentSerial, result, PayStatus.SUCCESS);
					}else{
						log.info("tradeNo："+paymentSerial.getOrderSn()+" refund message："+result);
					}
				}else{
					log.info("【订单退款查询:" + paymentSerial.getOrderSn() + "userId" + paymentSerial.getUserId() + "】...result :" + result);
				}
			} catch (Exception e) {
				log.error(e.getMessage(), e);
				return false;
			}
		}
		return false;
	}
	
	private Map<String, Object> getPluginConfigMap(PluginConfig pluginConfig) {
		Map<String, Object> pluginConfigMap = new HashMap<String, Object>();
		List<PluginConfigAttribute> pluginConfigAttributes = pluginConfigAttributeMapper
				.findByPluginId(pluginConfig.getId());
		for (int i = 0; i < pluginConfigAttributes.size(); i++) {
			PluginConfigAttribute pluginConfigAttribute = pluginConfigAttributes.get(i);
			pluginConfigMap.put(pluginConfigAttribute.getName(), pluginConfigAttribute.getAttributes());
		}
		return pluginConfigMap;
	}

}
